package com.htl.exampro.exam.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.htl.exampro.base.exception.ExamProException;
import com.htl.exampro.base.model.PageResult;
import com.htl.exampro.base.model.ServerResponse;
import com.htl.exampro.base.utils.UserUtil;
import com.htl.exampro.exam.mapper.ExamMapper;
import com.htl.exampro.exam.mapper.ExamResultMapper;
import com.htl.exampro.exam.pojo.constant.ExamStatusConstant;
import com.htl.exampro.exam.pojo.domain.ExamDO;
import com.htl.exampro.exam.pojo.domain.ExamHistoryDO;
import com.htl.exampro.exam.pojo.domain.ExamResultDO;
import com.htl.exampro.exam.pojo.query.ExamResultPageParam;
import com.htl.exampro.exam.pojo.vo.ExamResultVO;
import com.htl.exampro.exam.service.ExamResultService;
import com.htl.exampro.question.api.AnswerApi;
import com.htl.exampro.question.pojo.query.AnswerPageParam;
import com.htl.exampro.question.pojo.vo.AnswerDetailVO;
import com.htl.exampro.user.api.UserApi;
import com.htl.exampro.user.pojo.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 考试服务实现类
 */
@Service
public class ExamResultServiceImpl extends ServiceImpl<ExamResultMapper, ExamResultDO> implements ExamResultService {

    @Autowired
    UserApi userApi;
    @Autowired
    AnswerApi answerApi;

    @Autowired
    ExamMapper examMapper;

    @Override
    public ExamResultVO getExamResultDetail(String id) {
        return BeanUtil.toBean(getById(id), ExamResultVO.class);
    }

    @Override
    public PageResult<ExamResultVO> getExamHistory(ExamResultPageParam pageParam) {
        Page<ExamHistoryDO> page = new Page<>(pageParam.getPageNumber(), pageParam.getPageSize());
        pageParam.setUserId(UserUtil.getUserId());
        Page<ExamHistoryDO> doPage = baseMapper.getExamHistoryPage(page, pageParam);
        return PageResult.result(doPage, ExamResultVO.class);
    }


    @Override
    public int createExamResult(String examId) {
        ExamResultDO resultDO = baseMapper.selectOne(Wrappers.<ExamResultDO>lambdaQuery()
                .eq(ExamResultDO::getExamId, examId)
                .eq(ExamResultDO::getUserId, UserUtil.getUserId()));
        if (resultDO != null) {
            ExamProException.cast("已报名，请勿重复报名！");
        }
        ExamResultDO examResultDO = new ExamResultDO();
        examResultDO.setExamId(Long.valueOf(examId));
        examResultDO.setUserId(Long.valueOf(UserUtil.getUserId()));
        examResultDO.setStatusId(ExamStatusConstant.YBM.getStatusId());
        return baseMapper.insert(examResultDO);
    }

    @Override
    public int beginExamResult(String examId) {
        ExamResultDO resultDO = baseMapper.selectOne(Wrappers.<ExamResultDO>lambdaQuery()
                .eq(ExamResultDO::getExamId, examId)
                .eq(ExamResultDO::getUserId, UserUtil.getUserId()));
        if (resultDO == null) {
            ExamProException.cast("非法访问：考试未报名！");
        } else if (resultDO.getSubmitTime() != null) {
            ExamProException.cast("非法访问：试卷已提交！");
        } else if (ExamStatusConstant.YBM.getStatusId().equals(resultDO.getStatusId())) {
            LambdaUpdateWrapper<ExamResultDO> wrapper = Wrappers.lambdaUpdate();
            wrapper.set(ExamResultDO::getBeginTime, LocalDateTime.now());
            wrapper.set(ExamResultDO::getStatusId, ExamStatusConstant.KSZ.getStatusId());
            wrapper.eq(ExamResultDO::getExamId, Long.valueOf(examId));
            wrapper.eq(ExamResultDO::getUserId, Long.valueOf(UserUtil.getUserId()));
            return baseMapper.update(wrapper);
        }
        return 1;
    }

    @Override
    public int updateExamResult(String examId) {
        LambdaUpdateWrapper<ExamResultDO> wrapper = Wrappers.lambdaUpdate();
        wrapper.set(ExamResultDO::getSubmitTime, LocalDateTime.now());
        wrapper.set(ExamResultDO::getStatusId, ExamStatusConstant.YJJ.getStatusId());
        wrapper.eq(ExamResultDO::getExamId, Long.valueOf(examId));
        wrapper.eq(ExamResultDO::getUserId, Long.valueOf(UserUtil.getUserId()));
        return baseMapper.update(wrapper);
    }

    @Override
    public PageResult<ExamResultVO> getExamScoresPage(ExamResultPageParam pageParam) {
        Page<ExamResultDO> page = new Page<>(pageParam.getPageNumber(), pageParam.getPageSize());
        page(page, Wrappers.<ExamResultDO>lambdaQuery().eq(ExamResultDO::getExamId, pageParam.getExamId()));
        PageResult<ExamResultVO> result = PageResult.result(page, ExamResultVO.class);
        for (ExamResultVO vo : result.getData())
            vo.setDuration((int) Duration.between(vo.getBeginTime(), vo.getSubmitTime()).toMinutes());
        ServerResponse<List<UserVO>> response = userApi.getUsernameList(result.getData().stream().map(ExamResultVO::getUserId).distinct().toList());
        List<UserVO> userVOS = response.getResult();
        if (userVOS != null && !userVOS.isEmpty()) {
            Map<String, String> map = userVOS.stream().collect(Collectors.toMap(UserVO::getUserId, UserVO::getUsername));
            for (ExamResultVO vo : result.getData())
                vo.setUsername(map.get(vo.getUserId()));
        }
        return result;
    }

    @Override
    public List<AnswerDetailVO> getAnswerPage(Long examId, Long resultId) {
        ExamDO examDO = examMapper.selectById(examId);
        ExamResultDO resultDO = baseMapper.selectById(resultId);
        AnswerPageParam pageParam = new AnswerPageParam();
        pageParam.setExamId(examId);
        pageParam.setPaperId(examDO.getPaperId());
        pageParam.setUserId(resultDO.getUserId());
        ServerResponse<List<AnswerDetailVO>> response = answerApi.queryAnswerDetail(pageParam);
        return response.getResult();
    }

    @Override
    public Map<String, Object> getExamResults(Long examId) {
        ExamDO examDO = examMapper.selectById(examId);
        Map<String, Object> examInfo = new HashMap<>();
        examInfo.put("examId", examDO.getExamId());
        examInfo.put("examName", examDO.getExamName());
        examInfo.put("startTime", LocalDateTimeUtil.format(examDO.getStartTime(), "yyyy-MM-dd HH:mm:ss"));
        examInfo.put("endTime", LocalDateTimeUtil.format(examDO.getEndTime(), "yyyy-MM-dd HH:mm:ss"));
        examInfo.put("totalScore", examDO.getTotalScore());
        List<ExamResultDO> examResultDOS = baseMapper.selectList(Wrappers.<ExamResultDO>lambdaQuery().eq(ExamResultDO::getExamId, examId));
        examInfo.put("attendCount", examResultDOS.size());
        List<Integer> scores = examResultDOS.stream().map(ExamResultDO::getScore).filter(Objects::nonNull).toList();
        examInfo.put("avgScore", scores.stream().reduce(0, Integer::sum));
        examInfo.put("maxScore", scores.stream().max(Integer::compareTo).orElse(0));
        examInfo.put("minScore", scores.stream().min(Integer::compareTo).orElse(0));

        Map<String, Object> result = new HashMap<>();
        result.put("examInfo", examInfo);
        result.put("scoreDistribution", getScoreDistribution(examDO.getTotalScore(), scores));
        return result;
    }

    private static List<Map<String, Object>> getScoreDistribution(Integer totalScore, List<Integer> scores) {
        List<Map<String, Object>> resultList = new ArrayList<>();
        Integer[] scoreStage = {0, 0, 0, 0};
        scoreStage[0] = totalScore * 90 / 100;
        scoreStage[1] = totalScore * 80 / 100;
        scoreStage[2] = totalScore * 70 / 100;
        scoreStage[3] = totalScore * 60 / 100;
        Integer[] num = {0, 0, 0, 0, 0};
        for (Integer score : scores) {
            if (score > scoreStage[0]) num[0]++;
            if (score > scoreStage[1]) num[1]++;
            if (score > scoreStage[2]) num[2]++;
            if (score > scoreStage[3]) num[3]++;
            if (score < scoreStage[3]) num[4]++;
        }
        for (int i = 4; i >= 0; i--) {
            Map<String, Object> map = new HashMap<>();
            map.put("count", num[i]);
            if (i == 0) {
                map.put("range", scoreStage[i] + "-" + totalScore);
            } else if (i == 4) {
                map.put("range", "0-" + (scoreStage[i - 1] - 1));
            } else {
                map.put("range", scoreStage[i] + "-" + (scoreStage[i - 1] - 1));
            }
            resultList.add(map);
        }
        return resultList;
    }
}